import Logger from '../base/logger';
import {
    CCSClient,
    releaseCcsClient,
    validateCcsClient,
    ccsErrorEvent,
    sendTextMessage,
    onTextMessage,
    onResourceMessage,
    sendForm,
    onForm,
    onHoldOn,
    sendCustomMessage,
    onCustomMessage,
    connect,
    refreshCcsTicket,
    onInvitationProgress,
    quitSession,
    resumeCcsConnection,
    onJoinSession,
    onQuitSession,
    onScanCode,
    onRequestLocation
} from "./ccs_client";
import {
    customerLogin,
    refreshNewToken,
    createTokenWithRtmpServer,
    pushRtmpUrlToZms,
    addStreamToMixStream,
    subscribeRobotStream,
    getRtmpStat
} from "./apiMini";
import {
    addEventListener,
    dispatchEvent,
    removeEventListener,
    systemErrorCode,
    systemErrorName
} from "../base/event";
import {
    IRtcMiniClient
} from "./irtcMiniScript";
import {
    deepCopy,
    isEmptyString,
    isNullObject,
    validationStringEnd
} from "./tool";
import {
    IvcsError
} from "../base/error";


class Customer {
    constructor() {
        this.associatedUrls = {};
        this.requestSessionResult = {};
    }

    /**
     * 访客初始化
     * @param {string} backendUrl 访问的服务地址
     * @param {string} name 访客注册的用户名
     * @param {string} password  访客密码
     * @param {string} org 访客所属的机构
     * @param {string} rtmpServerUrl rtmp服务器的地址
     * @param {function} onSuccess 初始化成功
     * @param {function} onFailure 初始化失败
     */
    async init(backendUrl, name, password, org, rtmpServerUrl, onSuccess, onFailure) {
        if (backendUrl === undefined || backendUrl === "") {
            Logger.warning('backendUrl is empty');
            let error = new IvcsError('1000', 'backendUrl is empty');
            onFailure(error);
            return;
        }
        if (name === undefined || name === "") {
            Logger.warning('name is empty');
            let error = new IvcsError('1000', 'name is empty');
            onFailure(error);
            return;
        }
        if (password === undefined || password === "") {
            Logger.warning('password is empty');
            let error = new IvcsError('1000', 'password is empty');
            onFailure(error);
            return;
        }
        if (org === undefined || org === "") {
            Logger.warning('organization is empty');
            let error = new IvcsError('1000', 'organization is empty');
            onFailure(error);
            return;
        }
        if (isEmptyString(rtmpServerUrl)) {
            Logger.warning('rtmpServerUrl is empty');
            let error = new IvcsError('1000', 'rtmpServerUrl is empty');
            onFailure(error);
            return;
        }
        if (!validationStringEnd(rtmpServerUrl, '/')) {
            let error = new IvcsError('1000', `rtmpServerUrl is invalid,please add '/' at the end`);
            onFailure(error);
            return;
        }
        try {
            this.rtmpServerUrl = rtmpServerUrl;
            this.backendUrl = deepCopy(backendUrl);
            let login = await customerLogin(backendUrl, name, password, org);
            this.accessToken = (login.data && login.data.data && login.data.data.accessToken) ? login.data.data.accessToken : '';
            let tokenData = (login.data && login.data.data && login.data.data.tokenData) ? login.data.data.tokenData : null;
            this.userId = tokenData.data.id;
            this.ticket = (login.data && login.data.data && login.data.data.ticket) ? login.data.data.ticket : '';
            this.ccsClient = new CCSClient();
            this.associatedUrls = login.data.data.urls;
            this.ccsUrl = backendUrl ? backendUrl : login.data.data.urls.ccsurl;
            let refreshTicketTimeMs = (tokenData.exp - tokenData.iat - 1) > 0 ? (tokenData.exp - tokenData.iat - 1) * 1000 : 1000;
            let intervalMax = Math.pow(2, 31) - 1;
            refreshTicketTimeMs > intervalMax ? refreshTicketTimeMs = intervalMax : undefined;
            this.refreshTokenInterval = setInterval(async () => {
                try {
                    let newTokenTicket = await refreshNewToken(backendUrl, this.userId, this.ticket);
                    this.ticket = newTokenTicket.ticket;
                    this.accessToken = newTokenTicket.accessToken;
                    this.ccsClient.accessToken = newTokenTicket.accessToken;
                } catch (error) {
                    clearInterval(this.refreshTokenInterval);
                    onFailure(error);
                }
            }, refreshTicketTimeMs);
            onSuccess(login.data.data);
        } catch (error) {
            onFailure(error);
        }
    }

    /**
     * 游客初始化,适用于非注册用户,外系统的访客调用ivcs-backend的登录接口后返回相关的参数。
     * @param {string} backendUrl  请求的后台的地址
     * @param {object} associatedUrls  后台服务相关联地址
     * @param {string} accessToken  游客登录后返回的token
     * @param {object} tokenData  游客登录后返回的tokenData
     * @param {string} ticket  游客的token对应的刷新的ticket
     * @param {string} rtmpServerUrl rtmp服务器的地址
     * @param {function} onSuccess  游客初始化成功
     * @param {function} onFailure  游客初始化失败
     */
    async visitorInit(backendUrl, associatedUrls, accessToken, tokenData, ticket, rtmpServerUrl, onSuccess, onFailure) {
        if (isEmptyString(backendUrl)) {
            Logger.warning('backendUrl is empty');
            let error = new IvcsError('1000', 'backendUrl is empty');
            onFailure(error);
            return;
        }
        if (isEmptyString(accessToken)) {
            Logger.warning('accessToken is empty');
            let error = new IvcsError('1000', 'accessToken is empty');
            onFailure(error);
            return;
        }
        if (isEmptyString(ticket)) {
            Logger.warning('ticket is empty');
            let error = new IvcsError('1000', 'ticket is empty');
            onFailure(error);
            return;
        }
        if (isNullObject(associatedUrls)) {
            Logger.warning('associatedUrls is error');
            let error = new IvcsError('1000', 'associatedUrls is error');
            onFailure(error);
            return;
        }
        if (isEmptyString(rtmpServerUrl)) {
            Logger.warning('rtmpServerUrl is empty');
            let error = new IvcsError('1000', 'rtmpServerUrl is empty');
            onFailure(error);
            return;
        }
        if (!validationStringEnd(rtmpServerUrl, '/')) {
            let error = new IvcsError('1000', `rtmpServerUrl is invalid,please add '/' at the end`);
            onFailure(error);
            return;
        }
        this.accessToken = deepCopy(accessToken);
        if (!tokenData) {
            Logger.warning('tokenData is Invalid Signature');
            let error = new IvcsError('1000', 'tokenData is Invalid Signature');
            onFailure(error);
            return;
        }
        try {
            this.rtmpServerUrl = rtmpServerUrl;
            this.backendUrl = deepCopy(backendUrl);
            this.userId = tokenData.data.id;
            this.ticket = deepCopy(ticket);
            this.ccsClient = new CCSClient();
            this.associatedUrls = deepCopy(associatedUrls);
            this.ccsUrl = backendUrl ? backendUrl : associatedUrls.ccsurl;
            let refreshTicketTimeMs = (tokenData.exp - tokenData.iat - 1) > 0 ? (tokenData.exp - tokenData.iat - 1) * 1000 : 1000;
            let intervalMax = Math.pow(2, 31) - 1;
            refreshTicketTimeMs > intervalMax ? refreshTicketTimeMs = intervalMax : undefined;
            this.refreshTokenInterval = setInterval(async () => {
                try {
                    let newTokenTicket = await refreshNewToken(backendUrl, this.userId, this.ticket);
                    this.ticket = newTokenTicket.ticket;
                    this.accessToken = newTokenTicket.accessToken;
                    this.ccsClient.accessToken = newTokenTicket.accessToken;
                } catch (error) {
                    clearInterval(this.refreshTokenInterval);
                    onFailure(error);
                }
            }, refreshTicketTimeMs);
            onSuccess('ok');
        } catch (error) {
            onFailure(error);
        }
    }

    /**
     * 小程序发起呼叫坐席
     * @since 1.0.3版本新增API
     * @param {object} requestParameter  发起呼叫的参数，可自定义随路数据(userData)
     * @param {string} pushUrl 推流地址。目前仅支持 rtmp 格式
     * @param {boolean} isSecurity 是否安全校验url,默认校验。即在rtmp的url后面加上?token=
     * @param {number} validityTime url过期时间 单位分钟
     * @param {function} onSuccess 呼叫成功
     * @param {function} onFailure 呼叫失败
     * @param {function} onAgentJoin 坐席加入会话,返回加入坐席的个人信息和rtmp地址,对于人工坐席，小程序端直接用live-player组件播放,对于机器人坐席，小程序端调用subscribeRobot
     * @param {function} onAgentQuit 坐席离开会话,返回离开坐席的个人信息
     */
    async requestSession(requestParameter, pushUrl, isSecurity = true, validityTime = 30, onSuccess, onFailure, onAgentJoin, onAgentQuit) {
        if (isEmptyString(pushUrl)) {
            let error = new IvcsError('1000', `pushUrl is invalid`);
            onFailure(error);
            return;
        }
        if (isEmptyString(this.rtmpServerUrl)) {
            let error = new IvcsError('7000', `you need init`);
            onFailure(error);
            return;
        }
        // eslint-disable-next-line no-unused-vars
        let connectCcs = await connect(this.ccsClient, this.ccsUrl, this.accessToken, (error) => {
            onFailure(error);
        });
        ccsErrorEvent((info) => {
            dispatchEvent(systemErrorName, systemErrorCode.ccsSocketIoError, info);
        });
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
            return;
        }
        this.rtmpToken = "";
        if (isSecurity) {
            this.rtmpToken = await createTokenWithRtmpServer(this.backendUrl, this.accessToken, validityTime);
        }
        this.ccsClient.requestSession(requestParameter, async (status, resp) => {
            Logger.info('ivcs-js-sdk:request session result,', status, resp);
            if (status === 'ok') {
                this.requestSessionResult = resp;
                this.ccsClient.ticket = resp.ticket;
                let refreshTicketTime = resp.refersh_ticket_time;
                refreshCcsTicket(this.ccsClient, refreshTicketTime);
                this.ccsClient.members = resp.members;
                //joinroom
                this.pushUrl = deepCopy(pushUrl) + '?token=' + this.rtmpToken;
                this.pushUrlPath = pushUrl.substring(this.rtmpServerUrl.length);
                Logger.info('ivcs-js-sdk:pushUrl path', this.pushUrlPath);
                let turnServerUrl = this.associatedUrls.turnserverUrl;
                let turnServerCredential = this.associatedUrls.turnserverCredential;
                let turnServerUsername = this.associatedUrls.turnserverUsername;
                this.irtcMiniClient = new IRtcMiniClient(turnServerUrl, turnServerCredential, turnServerUsername);
                let roomInfo = await this.irtcMiniClient.joinRoom(this.requestSessionResult.roomToken);
                Logger.info('ivcs-js-sdk:roominfo', roomInfo);
                this.selfId = roomInfo.self.id;
                //user join quit event
                onJoinSession(this.ccsClient, (userInfo) => {
                    if (userInfo.data && userInfo.data.role === 'agent') {
                        let client_id = userInfo.data.id;
                        let sessionId = this.requestSessionResult.sessionId;
                        //the rules of agent stream-out url 
                        let url = this.rtmpServerUrl + client_id + sessionId + '?token=' + this.rtmpToken;
                        userInfo.data.url = url;
                        let userData = userInfo.data;
                        this.isRobotAgent = false;
                        if (userData && userData.user && userData.user.profile && userData.user.profile.isRobot) {
                            this.isRobotAgent = true;
                        }
                        onAgentJoin(userInfo);
                    }
                });
                onQuitSession(this.ccsClient, (userInfo) => {
                    if (userInfo.data.role === 'agent') {
                        onAgentQuit(userInfo);
                    }
                });
                onSuccess({
                    response: resp,
                    rtmpToken: this.rtmpToken,
                    pushUrl: this.pushUrl,
                    selfId: this.selfId
                });
            } else {
                onFailure(new IvcsError('5008', resp));
            }
        })
    }


    /**
     * 发起呼叫后，相关的进度回调，状态变化(一般在requestSession后就监听此回调方法)
     * @param {function} ack 呼叫的进度回调,状态数据
     */
    onInvitationProgress(ack) {
        onInvitationProgress(this.ccsClient, (resp) => {
            ack(resp);
        });
    }


    /**
     * 小程序:挂断会话
     * @since 1.0.3版本新增API
     * @param {object} reason 挂断原因,默认为{},传递格式{reason:"离开会话"}
     * @param {function} onSuccess 挂断成功
     * @param {function} onFailure 挂断失败
     */
    hangUp(reason = {}, onSuccess, onFailure) {
        let taskList = [];
        if (this.ccsClient && this.ccsClient.accessToken) {
            taskList.push(quitSession(reason, this.ccsClient))
        }
        if (this.irtcMiniClient) {
            taskList.push(this.irtcMiniClient.leaveRoom());
        }
        Promise.all(taskList).then((value) => {
            Logger.info('ivcs-js-sdk:hangUp success', value);
            releaseCcsClient(this.ccsClient);
            onSuccess(value);
        }).catch((error) => {
            Logger.info('ivcs-js-sdk:hangUp error', error);
            releaseCcsClient(this.ccsClient);
            onFailure(error);
        });
        clearInterval(this.refreshTokenInterval);
        this.selfId = null;
        this.pushUrl = null;
    }

    /**
     * 小程序推流，在live-pusher组件的status返回成功的时候/并且访客joinRoom成功后,开始推流
     * @since 1.0.3版本新增API
     * @param {function} onSuccess 成功，返回流id
     * @param {function} onFailure 失败
     */
    async livePush(onSuccess, onFailure) {
        try {
            if (!this.selfId || !this.pushUrl) {
                let error = new IvcsError('7001', 'before live push,you need joinRoom success');
                onFailure(error);
                return;
            }
            let attributes = {
                type: "customer"
            };
            let livePusher_success = await getRtmpStat(this.backendUrl, this.accessToken, this.pushUrlPath);
            if (livePusher_success) {
                Logger.info('ivcs-js-sdk:roomid,selfId', this.requestSessionResult.roomId, this.selfId);
                this.mini_streamId = await pushRtmpUrlToZms(this.backendUrl, this.requestSessionResult.roomId, this.selfId, this.pushUrl, attributes, true);
                // eslint-disable-next-line no-unused-vars
                let add = await addStreamToMixStream(this.backendUrl, this.requestSessionResult.roomId, this.mini_streamId, this.accessToken, true);
                onSuccess('ok');
            } else {
                let error = new IvcsError('7004', `live pusher hasn't worked yet`);
                onFailure(error);
                return;
            }
        } catch (error) {
            Logger.error('ivcs-js-sdk:live push error', error);
            onFailure(error);
        }
    }

    /**
     * 小程序端订阅机器人，需要在livePush成功后才能调用
     * @since 1.0.3版本新增API
     * @param {string} robotAgentRtmpUrl 呼叫坐席成功后，监听到坐席加入进来时的url
     * @param {function} onSuccess 订阅成功
     * @param {function} onFailure 订阅失败
     */
    async subscribeRobot(robotAgentRtmpUrl, onSuccess, onFailure) {
        try {
            if (!this.mini_streamId) {
                let error = new IvcsError('7003', 'before subscribe robot stream,you need livePush success');
                onFailure(error);
                return;
            }
            let result = await subscribeRobotStream(this.backendUrl, this.requestSessionResult.roomId, this.selfId, robotAgentRtmpUrl);
            Logger.info('ivcs-js-sdk:subscribeRobot result', result);
            let user = {};
            Logger.info('user participantId', this.selfId);
            this.ccsClient.readyToTalk(this.selfId, user, 'agents', async (status, resp) => {
                Logger.info(`ivcs-js-sdk:ready to talk robot agent response,${status},${resp},${new Date()}`);
                if (status == 'ok') {
                    onSuccess('ok');
                } else {
                    onFailure(new IvcsError('5005', `ready to talk robot agent error ,${status},${resp},${new Date()}`));
                }
            })
        } catch (error) {
            Logger.error('ivcs-js-sdk:subscribeRobot error', error);
            onFailure(error);
        }
    }

    /**
     * 告知对方(一般指机器人),访客音视频推送成功
     * @param {string} participantId 访客画面推送成功后返回的当前用户在room里的id(即roomInfo.self.id)
     * @param {object} user 访客自定义的一些个人信息（随路数据），如：头像，姓名，性别等
     * @param {string} toWho 告知消息的对象，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人
     * @param {function} onSuccess 消息发送成功
     * @param {function} onFailure 消息发送失败
     */
    readyToTalk(participantId, user, toWho, onSuccess, onFailure) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
            return;
        }
        this.ccsClient.readyToTalk(participantId, user, toWho, (status, resp) => {
            Logger.info(`ivcs-js-sdk:ready to talk ${toWho},success,',${resp},${new Date()}`);
            if (status === 'ok') {
                onSuccess(resp);
            } else {
                onFailure(new IvcsError('5005', resp));
            }
        })
    }

    /**
     * 收到对方音视频已准备好的消息
     * @param {function} ack 收到消息的回调
     */
    onReadyToTalk(ack) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            Logger.warning(`onReadyToTalk error,${JSON.stringify(isValid)}`);
            return;
        }
        this.ccsClient.onReadyToTalk((resp) => {
            ack(resp);
        });
    }

    /**
     * 访客会话被系统强制结束,网络异常导致服务结束
     * @param {function} ack 返回具体的原因
     */
    onDrop(ack) {
        let dropMessage = {
            drop: {
                code: 401,
                message: 'connect forced termination drop'
            },
            error: {
                code: 402,
                message: 'socket io connect error'
            }
        };
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            Logger.warning(`onDrop error,${JSON.stringify(isValid)}`);
            return;
        }
        this.ccsClient.onDrop((resp) => {
            releaseCcsClient(this.ccsClient);
            Logger.warning('ccs connect forced termination drop', resp, new Date());
            ack(dropMessage.drop);
        });
        this.ccsClient.onError(async (data) => {
            if (data && data.code === '1101') {
                //token expired
                let backendUrl = this.associatedUrls.backendurl;
                let newTokenTicket = await refreshNewToken(backendUrl, this.userId, this.ticket);
                this.ticket = newTokenTicket.ticket;
                this.accessToken = newTokenTicket.accessToken;
                // eslint-disable-next-line no-unused-vars
                let newConnect = await connect(this.ccsClient, this.ccsUrl, this.accessToken);
                // eslint-disable-next-line no-unused-vars
                let resumeConnect = resumeCcsConnection(this.ccsClient, this.ccsClient.ticket);
            } else {
                releaseCcsClient(this.ccsClient);
                ack(dropMessage.error);
            }
        })
    }

    /**
     * 收到手写签名的事件消息
     * @param {function} ack 事件回调数据
     */
    onHandSignatureRequest(ack) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            Logger.warning(`onHandSignatureRequest error,${JSON.stringify(isValid)}`);
            return;
        }
        this.ccsClient.onHandSignature((resp) => {
            ack(resp);
        })
    }

    /**
     * 发送手写签名相关数据（轨迹等）
     * @param {object} data  自定义数据
     * @param {string} toWhom  告知消息的对象，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'agents'
     * @param {function} onSuccess 成功
     * @param {function} onFailure 失败
     */
    sendHandSignMessage(data, toWhom = 'agents', onSuccess, onFailure) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
            return;
        }
        // eslint-disable-next-line no-unused-vars
        this.ccsClient.handSign(false, data, toWhom, (status, resp, msgId) => {
            if (status === "ok") {
                onSuccess(resp);
            } else {
                onFailure(resp);
            }
        })
    }

    /**
     * 收到屏幕截图消息
     * @param {function} ack 事件回调数据
     */
    onScreenSnapshotRequest(ack) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            Logger.warning(`onScreenSnapshotRequest error,${JSON.stringify(isValid)}`);
            return;
        }
        this.ccsClient.onScreenSnapshot((resp) => {
            ack(resp);
        })
    }

    /**
     * 发送文字消息
     * @param {string} message 消息内容
     * @param {string} clientId 接受消息的用户，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'agents';或者具体用户的clientId
     * @param {function} onSuccess 成功
     * @param {function} onFailure 失败
     */
    sendTextMessage(message, clientId = 'agents', onSuccess, onFailure) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
            return;
        }
        // eslint-disable-next-line no-unused-vars
        sendTextMessage(this.ccsClient, message, clientId, (resp) => {
            onSuccess();
        }, (error) => {
            onFailure(new IvcsError('1004', error));
        });
    }

    /**
     * 收到文字消息
     * @param {function} ack 具体的消息数据
     */
    onTextMessage(ack) {
        onTextMessage(this.ccsClient, (resp) => {
            ack(resp);
        })
    }

    /**
     * 根据文件的url发送文件。
     * @param {string} type 文件类型 支持:'doc', 'pic', 'video', 'audio', 'page', 'paper'
     * @param {string} url 文件相应的url地址
     * @param {string} toRole 接受文件的用户，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'agents';
     * @param {string} fileName 文件名
     * @param {function} onSuccess 成功
     * @param {function} onFailure 失败
     */
    async shareResourceWithUrl(type, url, toRole = 'agents', fileName = "", onSuccess, onFailure) {
        let supportType = ['doc', 'pic', 'video', 'audio', 'page', 'paper'];
        if (supportType.indexOf(type) === -1) {
            let error = new IvcsError('2001', `type is invalid,only support 'doc','pic', 'video', 'audio', 'page', 'paper'`);
            onFailure(error);
            return;
        }
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
            return;
        }
        try {
            this.ccsClient.shareResource(type, url, fileName, toRole, (status, resp, msgId) => {
                if (status === 'ok') {
                    onSuccess();
                } else {
                    Logger.info(`ivcs-js-sdk:send shareResource message failed,${resp},msgId${msgId},${new Date()}`);
                    onFailure(new IvcsError('1009', resp));
                }
            });
        } catch (error) {
            onFailure(error);
        }
    }

    /**
     * 收到资源文件信息（图片，文档等）
     * @param {function} ack 返回具体的资源文件数据
     */
    onResourceMessage(ack) {
        onResourceMessage(this.ccsClient, (resp) => {
            ack(resp);
        })
    }

    /**
     * 提交表单数据
     * @param {object} formTemplate 表单具体内容
     * @param {string} clientId  接受消息的用户，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'agents';
     * @param {function} onSuccess 成功
     * @param {function} onFailure 失败
     */
    submitFormData(formTemplate, clientId = 'agents', onSuccess, onFailure) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
            return;
        }
        sendForm(this.ccsClient, formTemplate, clientId, (resp) => {
            onSuccess(resp);
        }, (error) => {
            onFailure(new IvcsError('1005', error));
        });
    }

    /**
     * 收到表单数据
     * @param {function} ack 表单具体的内容
     */
    onForm(ack) {
        onForm(this.ccsClient, (resp) => {
            ack(resp);
        })
    }

    /**
     * 收到坐席正在转接中的消息，坐席转接给其他坐席触发该事件。
     * @param {function} ack 事件回调
     */
    onHoldOn(ack) {
        onHoldOn(this.ccsClient, (resp) => {
            ack(resp);
        })
    }

    /**
     * 发送自定义消息
     * @param {string|object} data 自定义的消息内容
     * @param {string} toRoles 发送消息的用户，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'agents';
     * @param {function} onSuccess 成功
     * @param {function} onFailure 失败
     */
    sendCustomMessage(data, toRoles = 'agents', onSuccess, onFailure) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
            return;
        }
        sendCustomMessage(this.ccsClient, data, toRoles, (resp) => {
            onSuccess(resp);
        }, (error) => {
            onFailure(new IvcsError('1006', error));
        })
    }

    /**
     * 收到自定义消息
     * @param {function} ack 返回具体的消息内容
     */
    onCustomMessage(ack) {
        onCustomMessage(this.ccsClient, (data) => {
            ack(data);
        })
    }

    /**
     * 发送确认消息，告知对方相应的确认信息
     * @param {string|object} message 确认消息的具体内容
     * @param {string} toRole 发送消息的用户，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'agents';
     * @param {function} onSuccess 成功
     * @param {function} onFailure 失败
     */
    sendMessageConfirmation(message, toRole = 'agents', onSuccess, onFailure) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            onFailure(isValid);
        }
        // eslint-disable-next-line no-unused-vars
        this.ccsClient.sendMessageConfirmation(message, toRole, (status, resp, msgId) => {
            if (status === 'ok') {
                onSuccess();
            } else {
                onFailure(new IvcsError('1017', resp));
            }
        })
    }

    /**
     * 收到信息确认消息
     * @param {function} ack 返回信息确认的具体数据
     */
    onMessageConfirmation(ack) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            Logger.warning(`onMessageConfirmation error,${JSON.stringify(isValid)}`);
            return;
        }
        this.ccsClient.onMessageConfirmation((data) => {
            ack(data);
        })
    }

    /**
     * 收到转人工的消息
     * @param {function} ack  返回转人工的原因消息
     */
    onTransferReasonMessage(ack) {
        let isValid = validateCcsClient(this.ccsClient);
        if (isValid != 'ok') {
            Logger.warning(`onTransferReasonMessage error,${JSON.stringify(isValid)}`);
            return;
        }
        this.ccsClient.onTransferReason((data) => {
            ack(data);
        })
    }

    /**
     * 添加事件监听
     * @param {string} eventName  事件名，目前系统支持的事件有:onCustomerQuitSession,onAgentQuitSession,onAgentJoinSession,systemError
     * @param {function} ack 事件返回具体的数据
     * @returns {function} 返回事件函数
     */
    addEventListener(eventName, ack) {
        Logger.info(`ivcs-js-sdk:customer add event listener with Name,${eventName}`);
        return addEventListener(eventName, ack);
    }

    /**
     * 移除事件监听
     * @param {string} eventName 事件名
     * @param {function} eventImplementationFunction  添加事件监听对应的返回的事件函数
     */
    removeEventListener(eventName, eventImplementationFunction) {
        Logger.info(`ivcs-js-sdk:customer remove event listener with Name,${eventName},with implementation function,${eventImplementationFunction}`);
        removeEventListener(eventName, eventImplementationFunction);
    }

    /**
     * 收到扫码指令
     * @since 1.0.4版本新增API
     * @param {function} ack 收到指令回调
     */
    onScanCode(ack) {
        onScanCode(this.ccsClient, (rsponse) => {
            ack(rsponse);
        })
    }

    /**
     * 收到获取地理位置指令
     * @since 1.0.4版本新增API
     * @param {function} ack 收到指令回调
     */
    onRequestLocation(ack) {
        onRequestLocation(this.ccsClient, (response) => {
            ack(response);
        })
    }
}

export { Customer };
